package com.svm.main;

import java.io.BufferedWriter;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.util.Collections;
import java.util.List;
import java.util.Scanner;

import com.svm.bean.Image;
import com.svm.bean.Parameter;
import com.svm.bean.SvmClass;
import com.svm.math.EuclidianDistance;
import com.svm.operators.Classification;
import com.svm.operators.Trainning;
import com.svm.operators.FeatureExtraction;
import com.svm.operators.Predicition;
import com.svm.util.FileUtils;
import com.svm.util.SvmClassUtils;

public class Main {

	private Long initialTime;
	private Long finalTime;

	public static void main(String[] args) {
		Main main = new Main();
		main.run(args);
	}

	public void run(String[] args) {
		try {

			Parameter param = this.readArguments(args);
			FileUtils.configEnvironment();

			switch (param.getFase()) {
			case 'c':
				this.classification(param);
				break;
			case 'q':
				this.query(param);
				break;
			case 't':
				this.trainnig(param);
				break;
			case 'd':
				this.delete();
				break;
			default:
				this.list();
				break;
			}

		} catch (IllegalArgumentException ex) {
			this.argumentsHelp();
		}
	}

	private void query(Parameter param) {
		this.print("query fase");
		this.initialTime = System.currentTimeMillis();

		param.setClassName("query");
		FeatureExtraction extraction = new FeatureExtraction(param);
		Image queryImage = extraction.extract();

		Predicition pred = new Predicition(queryImage, param);

		this.print("predicition");

		SvmClass svmClass = pred.predict();

		for (Image img : svmClass.getImages()) {
			EuclidianDistance ec = new EuclidianDistance();
			img.setEuclidianDistance(ec.distance(queryImage, img));
		}

		Collections.sort(svmClass.getImages());

		this.showImages(svmClass, param);
		FileUtils.cleanTmpDir();

		this.finalTime = System.currentTimeMillis();

		this.print("done");
		this.print("Execution time:" + (finalTime - initialTime) / 1000 + "s");

	}

	private void list() {
		if (SvmClassUtils.listSvmClass().size() == 0) {
			this.print("no results");
		}

		for (SvmClass svm : SvmClassUtils.listSvmClass()) {
			String msg;

			msg = "Name:" + svm.getName() + "\n";
			msg += "Features DimenX:" + svm.getFeatDimenX() + "\n";
			msg += "Features DimenY:" + svm.getFeatDimenY() + "\n";
			msg += "Comp Algorithm:" + svm.getCompAlgorithm() + "\n";
			msg += "Number of images:" + svm.getImages().size() + "\n";
			msg += "**********************";

			this.print(msg);
		}
	}

	private void delete() {
		this.print("Do you really want to clean your trainning (y) yes or (n) no");

		Scanner sc = new Scanner(System.in);

		if (sc.nextLine().equalsIgnoreCase("y")) {
			FileUtils.deleteFiles();
			this.print("done");
		} else {
			this.print("finishing the program");
		}

		sc.close();
	}

	private void trainnig(Parameter param) {
		this.print("trainning your base");
		this.initialTime = System.currentTimeMillis();

		Trainning cla = new Trainning(SvmClassUtils.listSvmClass(), param);
		cla.train();

		this.finalTime = System.currentTimeMillis();

		this.print("*********************");
		this.print("done");
		this.print("Execution time:" + (finalTime - initialTime) / 1000 + "s");
	}

	private void classification(Parameter param) {

		this.print("Classification");
		this.initialTime = System.currentTimeMillis();

		Classification classification = new Classification(param);
		classification.classify();

		this.finalTime = System.currentTimeMillis();
		this.print("done");
		this.print("Execution time:" + (finalTime - initialTime) / 1000 + "s");
	}

	private Parameter readArguments(String[] args)
			throws IllegalArgumentException {

		Parameter parameter = new Parameter();
		parameter.setFase(args[0].charAt(1));

		if (parameter.getFase() == 'c') {

			if (args.length < 6) {
				throw new IllegalArgumentException();
			}

			parameter.setImagesFile(new File(args[1]));
			parameter.setFeatDimenY(Integer.parseInt(args[2]));
			parameter.setFeatDimenX(Integer.parseInt(args[3]));
			parameter.setCompAlgorithm(args[4].substring(1));
			parameter.setClassName(args[5]);

		} else if (parameter.getFase() == 'q') {

			if (args.length < 6) {
				throw new IllegalArgumentException();
			}

			parameter.setImagesFile(new File(args[1]));
			parameter.setOutputFile(new File(args[2]));
			parameter.setFeatDimenY(Integer.parseInt(args[3]));
			parameter.setFeatDimenX(Integer.parseInt(args[4]));
			parameter.setCompAlgorithm(args[5].substring(1));

		}

		return parameter;
	}

	private void showImages(SvmClass svmClass, Parameter parameter) {

		try {

			FileWriter writer = new FileWriter(parameter.getOutputFile());
			BufferedWriter bufferedWriter = new BufferedWriter(writer);

			bufferedWriter.write("Absolute class path:"
					+ svmClass.getAbsolutePath());
			bufferedWriter.newLine();
			bufferedWriter.write("Class name:" + svmClass.getName());
			bufferedWriter.newLine();
			bufferedWriter.write("Comp algorithm:"
					+ svmClass.getCompAlgorithm());
			bufferedWriter.newLine();
			bufferedWriter.write("Feature Dimension X:"
					+ svmClass.getFeatDimenX());
			bufferedWriter.newLine();
			bufferedWriter.write("Feature Dimension Y:"
					+ svmClass.getFeatDimenY());
			bufferedWriter.newLine();

			List<Image> images = svmClass.getImages();

			for (Image img : images) {
				bufferedWriter.write(img.getName());
				bufferedWriter.write(",");
			}
			bufferedWriter.close();
			writer.close();

		} catch (IOException ex) {
			this.print(ex);
		}

	}

	private void argumentsHelp() {

		String msg = "-(c, r) if is classification or retrieve\n"
				+ "-(Path)images directory for classification or image's path for retrieve\n"
				+ "-(Path)output file path\n"
				+ "-(Number)features dimension y\n"
				+ "-(Number)features dimension x\n"
				+ "-(DCT or FFT) compression algorithm\n"
				+ "-(String) class name";

		this.print(msg);

	}

	private void print(Object msg) {
		System.out.println(msg);
	}
}
